/* ***** BEGIN LICENSE BLOCK *****
 *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Youtube Tooltip.
 *
 * The Initial Developer of the Original Code is
 * Brandon Peters.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

let Ci = Components.interfaces;
let Cc = Components.classes;

var gPrefs = {};
var gYTPrefService = null;
var gOriginalTooltip = null;

var gPrefObserver = {
	register: function()
	{
		var prefService = Cc["@mozilla.org/preferences-service;1"]
			.getService(Ci.nsIPrefBranch);

		this._prefBranch = prefService.getBranch('extensions.{D48A12C2-E0F4-4640-8ADE-2DE4959E0F3D}.');
		this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
		this._prefBranch.addObserver('', this, false);
	},
	unregister: function()
	{
		if(!this._prefBranch)
		{
			return;
		}

		this._prefBranch.removeObserver('', this);
	},
	observe: function(aSubject, aTopic, aData)
	{
		if(aTopic != 'nsPref:changed')
		{
			return;
		}

		switch(this._prefBranch.getPrefType(aData))
		{
			case Ci.nsIPrefBranch.PREF_STRING:
				gPrefs[aData] = this._prefBranch.getCharPref(aData);
				break;
			case Ci.nsIPrefBranch.PREF_INT:
				gPrefs[aData] = this._prefBranch.getIntPref(aData);
				break;
			case Ci.nsIPrefBranch.PREF_BOOL:
				gPrefs[aData] = this._prefBranch.getBoolPref(aData);
				break;
		}
	}
};

function YoutubeTooltipStartup()
{
	gYTPrefService = Cc["@mozilla.org/preferences-service;1"]
		.getService(Ci.nsIPrefService)
		.getBranch('extensions.{D48A12C2-E0F4-4640-8ADE-2DE4959E0F3D}.');

	gYTPrefService.QueryInterface(Ci.nsIPrefBranch2);

	var preferences = [
		'showauthor',
		'showrating',
		'showviews',
		'showduration',
		'showdate',
		'showdescription',
		'showcategory',
		'showtags',
		'showcomments',
		'showresponses'
	];

	for(var idx in preferences)
	{
		var preference = preferences[idx];

		try
		{
			gPrefs[preference] = gYTPrefService.getBoolPref(preference);
		}
		catch(ex)
		{
			gPrefs[preference] = false;
		}
	}

	gPrefObserver.register();

	gOriginalTooltip = window.FillInHTMLTooltip;
	window.FillInHTMLTooltip = youtubeTooltip;
}

function YoutubeTooltipShutdown()
{
	gPrefObserver.unregister();
}

function youtubeTooltip(tipElement)
{
	var retVal = false;

	if(!tipElement)
	{
		return retVal;
	}

	if(tipElement.namespaceURI == 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul')
	{
		return retVal;
	}

	const XLinkNS = 'http://www.w3.org/1999/xlink';

	var linkHREF = null;
	var XLinkHREF = null;
	var direction = tipElement.ownerDocument.dir;

	linkHREF = tipElement.getAttribute('href');
	XLinkHREF = tipElement.getAttributeNS(XLinkNS, 'href');

	var defView = tipElement.ownerDocument.defaultView;
	if(!defView)
	{
		return retVal;
	}
	direction = defView.getComputedStyle(tipElement, '').getPropertyValue('direction');

	var hrefs = [linkHREF, XLinkHREF];
	var tipNode = document.getElementById('aHTMLTooltip');
	tipNode.style.direction = direction;

	var patterns = [
		[/^http:\/\/([A-Za-z0-9]*)(\.?)youtube\.com\/watch\?v=([A-Za-z0-9-_]+)/, 3],
		[/^\/watch\?v=([A-Za-z0-9-_]+)/, 1]
	];

	for(var i = 0; i < hrefs.length; ++i)
	{
		var h = hrefs[i];
		for(var j = 0; j < patterns.length; ++j)
		{
			var re = patterns[j][0];
			var idx = patterns[j][1];
			if(re.test(h))
			{
				var hrefArray = re.exec(h);

				var yID = hrefArray[idx];
				var yData = youtubeData(yID);
	
				var tipLabel = yData.title;
				if(gPrefs['showauthor'])
				{
					tipLabel += "\nAuthor: " + yData.author;
				}
		
				if(gPrefs['showduration'])
				{
					tipLabel += "\nDuration: " + yData.duration;
				}
	
				if(gPrefs['showrating'])
				{
					tipLabel += "\nRating: " + yData.rating;
				}
	
				if(gPrefs['showviews'])
				{
					tipLabel += "\nViews: " + yData.views;
				}
	
				if(gPrefs['showdate'])
				{
					tipLabel += "\nDate: " + yData.date;
				}
	
				if(gPrefs['showdescription'])
				{
					tipLabel += "\nDescription: " + yData.description;
				}
	
				if(gPrefs['showcategory'])
				{
					tipLabel += "\nCategory: " + yData.category;
				}
	
				if(gPrefs['showtags'])
				{
					tipLabel += "\nTags: " + yData.tags;
				}
	
				if(gPrefs['showcomments'])
				{
					tipLabel += "\nComments: " + yData.comments;
				}
	
				if(gPrefs['showresponses'])
				{
					tipLabel += "\nResponses: " + yData.responses;
				}
	
				if(tipLabel != null && yData.title != null)
				{
					tipNode.setAttribute('label', tipLabel);
					retVal = true;
				}
			}
		}
	}

	if(!retVal)
	{
		return gOriginalTooltip(tipElement);
	}

	return retVal;
}

function youtubeData(videoID)
{
	var yData = {
		title: 'Video No Longer Available',
		author: 'Unknown',
		rating: 'Unknown',
		views: 'Unknown',
		duration: 'Unknown',
		date: 'Unknown',
		description: 'Unknown',
		category: 'Unknown',
		tags: 'Unknown',
		comments: 'Unknown',
		responses: 'Unknown'
	};

	const xmlNS = 'http://www.w3.org/2005/Atom';
	const ytNS = 'http://gdata.youtube.com/schemas/2007'; 
	const gdNS = 'http://schemas.google.com/g/2005';
	const mediaNS = 'http://search.yahoo.com/mrss/';
	const osNS = 'http://a9.com/-/spec/opensearchrss/1.0/';

	var gdataURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID;
	var commentURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/comments';
	var responseURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/responses';

	var req = new XMLHttpRequest();
	req.open("GET", gdataURL, false);
	req.send(null);

	if(req.status != 200)
	{
		return yData;
	}

	var videoDom = req.responseXML;	
	if(videoDom.documentElement.nodeName == 'parseerror')
	{
		return yData;
	}

	/* Title */
	var titles = videoDom.getElementsByTagNameNS(xmlNS, 'title');
	if(titles)
	{
		var titleElement = titles[0];
		if(titleElement && titleElement.textContent)
		{
			yData.title = titleElement.textContent;
		}
	}

	/* Author */
	var authors = videoDom.getElementsByTagNameNS(xmlNS, 'author');
	if(authors)
	{
		var authorElement = authors[0];
		if(authorElement && authorElement.firstChild)
		{
			var nameElement = authorElement.firstChild;
			if(nameElement && nameElement.textContent)
			{
				yData.author = nameElement.textContent;
			}
		}
	}

	/* Rating */
	var ratings = videoDom.getElementsByTagNameNS(gdNS, 'rating');
	if(ratings)
	{
		var ratingElement = ratings[0];
		if(ratingElement && ratingElement.getAttribute('average'))
		{
			yData.rating = Math.round(ratingElement.getAttribute('average') * 100) / 100;
		}
	}

	/* Number of Views */
	var views = videoDom.getElementsByTagNameNS(ytNS, 'statistics');
	if(views)
	{
		var viewElement = views[0];
		if(viewElement && viewElement.getAttribute('viewCount'))
		{
			yData.views = addCommas(viewElement.getAttribute('viewCount'));
		}
	}

	/* Duration */
	var durations = videoDom.getElementsByTagNameNS(ytNS, 'duration');
	if(durations)
	{
		var durationElement = durations[0];
		if(durationElement && durationElement.getAttribute('seconds'))
		{
			yData.duration = '';

			var seconds = durationElement.getAttribute('seconds');
			var minutes = 0;
			var hours = 0;

			if(seconds > 60)
			{
				minutes = (seconds - (seconds % 60)) / 60;
				seconds = seconds % 60;
			}

			if(minutes > 60)
			{
				hours = (minutes - (minutes % 60)) / 60;
				minutes = minutes % 60;
			}

			if(hours != 0)
			{
				yData.duration += hours + 'h';
			}

			if(minutes != 0)
			{
				yData.duration += minutes + 'm';
			}

			if(seconds != 0)
			{
				yData.duration += seconds + 's';
			}
		}
	}

	/* Date Published */
	var dates = videoDom.getElementsByTagNameNS(xmlNS, 'published');
	if(dates)
	{
		var dateElement = dates[0];
		if(dateElement && dateElement.textContent)
		{
			var re = '([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})';
			var dateStr = dateElement.textContent;
			var matches = dateStr.match(new RegExp(re));
			if(matches)
			{
				yData.date = matches[2] + '/' + matches[3] + '/' + matches[1] + ' ' + matches[4] + ':' + matches[5] + ':' + matches[6];
			}
		}
	}

	/* Description */
	var descriptions = videoDom.getElementsByTagNameNS(xmlNS, 'content');
	if(descriptions)
	{
		var descriptionElement = descriptions[0];
		if(descriptionElement && descriptionElement.textContent)
		{
			yData.description = descriptionElement.textContent;
		}
	}
	
	/* Category */
	var categories = videoDom.getElementsByTagNameNS(mediaNS, 'category');
	if(categories)
	{
		var categoryElement = categories[0];
		if(categoryElement && categoryElement.textContent)
		{
			yData.category = categoryElement.textContent;
		}
	}

	/* Tags */
	var tags = videoDom.getElementsByTagNameNS(mediaNS, 'keywords');
	if(tags)
	{
		var tagElement = tags[0];
		if(tagElement && tagElement.textContent)
		{
			yData.tags = tagElement.textContent;
		}
	}

	/* Comments Entry */
	var req = new XMLHttpRequest();
	req.open("GET", commentURL, false);
	req.send(null);

	if(req.status != 200)
	{
		return yData;
	}

	var commentDom = req.responseXML;	

	//check responseStatus
	if(commentDom.documentElement.nodeName == 'parseerror')
	{
		return yData;
	}

	/* Number of Comments */
	var comments = commentDom.getElementsByTagNameNS(osNS, 'totalResults');
	if(comments)
	{
		var commentElement = comments[0];
		if(commentElement && commentElement.textContent)
		{
			yData.comments = commentElement.textContent;
		}
	}

	/* Responses Entry */
	var req = new XMLHttpRequest();
	req.open("GET", responseURL, false);
	req.send(null);

	if(req.status != 200)
	{
		return yData;
	}

	var responseDom = req.responseXML;	

	//check responseStatus
	if(responseDom.documentElement.nodeName == 'parseerror')
	{
		return yData;
	}

	/* Number of Responses */
	var responses = responseDom.getElementsByTagNameNS(osNS, 'totalResults');
	if(responses)
	{
		var responseElement = responses[0];
		if(responseElement && responseElement.textContent)
		{
			yData.responses = responseElement.textContent;
		}
	}

	return yData;
}

function addCommas(num)
{
 	var re = /(\d+)(\d{3})/;
	while(re.test(num))
	{
		num = num.replace(re, '$1' + ',' + '$2');
	}
	return num;
}

window.addEventListener('load', function(e) { YoutubeTooltipStartup(e); }, false);
window.addEventListener('unload', function(e) { YoutubeTooltipShutdown(e); }, false);
